#pragma once
#include "Business/Business.h"
#include "Business/DealingHistory.h"
#include "Business/CustomerPosition.h"
#include "Business/Leg.h"
#include "StatusMachine/StatusMachine.h"
#include "Business/Dealing.h"
#include "Business/Band.h"
#include "Business/Policy/ClassicBusinessPosition.h"
#include "Business/ArbitrageLegs.h"
#include "Business/ContractMatcher.h"

class Customer;
class Funds;
class MarketStorage;
class QueueContext;
class DealingRepository;
class CancellationRepository;

class ArbitrageBusiness;

namespace ArbitrageBusinessStatusMachine {
	class Event
	{
	public:
		enum TYPE {NONE,MARKET_EVENT,PENDING_MARKET_EVENT, DEALING_EVENT,START_COMMAND, EDIT_COMMAND,CLEAR_COMMAND,STOP_COMMAND};
		explicit Event(TYPE type = NONE);
		explicit Event(PendingMarket const& pendingMarket);
		explicit Event(Market const& market);
		explicit Event(Dealing const& dealing);
		int GetType() const { return m_type; }
		PendingMarket const& GetPendingMarket() const {return *(PendingMarket*)m_data;}
		Market const& GetMarket() const { return *(Market*)m_data; }
		Dealing const& GetDealing() const { return *(Dealing*)m_data; }

	private:
		int m_type;
		char m_data[1024];
	};

	typedef Status<ArbitrageBusiness,Event> StatusT;
	class StatusBase : public StatusT
	{
	public:
		StatusBase(std::string const& name, ArbitrageBusiness& context):StatusT(name,context){}
	private:
		virtual void DoBefore();
	};
	typedef ConditionBase<ArbitrageBusiness,Event> ConditionT;
	typedef StatusMachine<ArbitrageBusiness,Event> StatusMachineT;


#define DEFINE_CONDITION(condition_name)						\
	class condition_name : public ConditionT					\
	{															\
	public:														\
	explicit condition_name(ArbitrageBusiness& context)	\
	:ConditionT(context){}										\
	virtual ~condition_name() {}								\
	private:													\
	virtual bool DoCheck(Event const& event);					\
	}

	DEFINE_CONDITION(OpenMarketAndEnoughFundCondition);
	DEFINE_CONDITION(MinorOpenOrderedCondition);
	DEFINE_CONDITION(MinorOpenAllCancelledCondition);
	DEFINE_CONDITION(MinorOpenedCondition);

	DEFINE_CONDITION(OpenMarketCondition);
	DEFINE_CONDITION(LostOpenMarketCondition);

	DEFINE_CONDITION(MinorOffsetOrderedCondition);
	DEFINE_CONDITION(MinorAllOffsettedCondition);
	DEFINE_CONDITION(MinorOffsettedCondition);
	DEFINE_CONDITION(MinorOffsetAllCancelledCondition);

	DEFINE_CONDITION(MajorOpenOrderedCondition);
	DEFINE_CONDITION(MajorOpenAllCancelledCondition);
	DEFINE_CONDITION(MajorOpenedCondition);

	DEFINE_CONDITION(OffsetMarketCondition);
	DEFINE_CONDITION(StopMarketCondition);
	DEFINE_CONDITION(PartialOffsetedCondition);
	DEFINE_CONDITION(AllOffsetedCondition);

	DEFINE_CONDITION(StartCondition);
	DEFINE_CONDITION(ClearCondition);
	DEFINE_CONDITION(StopCondition);
	DEFINE_CONDITION(EditCondition);
#undef DEFINE_CONDITION

#define DEFINE_STATUS(class_name,name)					\
	class class_name : public StatusBase					\
	{														\
	public:													\
	explicit class_name(ArbitrageBusiness& context)		\
	:StatusBase(name,context){}							\
	virtual ~class_name(){}								\
	private:											\
	virtual void DoExecute(Event const& event);			\
	}

	DEFINE_STATUS(StartStatus,"start");
	DEFINE_STATUS(MinorOpenSentStatus,"minor open sent");
	DEFINE_STATUS(MinorOpenCancellationSentStatus,"minor open cancellation sent");
	DEFINE_STATUS(MarketCheckStatus,"market check");
	DEFINE_STATUS(MinorOffsetSentStatus,"minor offset sent");
	DEFINE_STATUS(MinorOffsetCancellationSentStatus,"minor offset cancellation sent");

	DEFINE_STATUS(MajorOpenSentStatus,"major open sent");
	DEFINE_STATUS(MajorOpenCancellationSentStatus,"major open cancellation sent");

	DEFINE_STATUS(OpenedStatus,"opened");


	DEFINE_STATUS(MajorOffsetSentStatus,"major offset sent");

	DEFINE_STATUS(MinorStopSentStatus,"minor stop sent");
	DEFINE_STATUS(MinorStopCancelSentStatus,"minor stop ordered");
	DEFINE_STATUS(MajorStopSentStatus,"major stop sent");

	DEFINE_STATUS(ClearSentStatus,"clear sent");
	DEFINE_STATUS(StopStatus,"stop");
	DEFINE_STATUS(EditableStatus,"editable");
#undef DEFINE_STATUS
}

class Dealing;
class Cancellation;
class TimeoutNotifier;
class Bollingers;

class ArbitrageBusinessArgument
{
public:

	enum TYPE {AUTO,BOTH,JUST_BEARISH,JUST_BULLISH};

	ArbitrageBusinessArgument(ArbitrageContracts const& contract,
		double openBalance, double profitBalance,int maxOpenQuantity)
		:m_type(AUTO),m_contract(contract),m_openBalance(openBalance),m_profitBalance(profitBalance),
		m_bearish(Band::CreateBearish()),m_bullish(Band::CreateBullish()),m_maxOpenQuantity(maxOpenQuantity)
	{
	}

	ArbitrageBusinessArgument(ArbitrageContracts const& contract,
		Band const& bearish, Band const& bullish, int maxOpenQuantity)
		:m_type(BOTH),m_contract(contract),m_openBalance(0),m_profitBalance(0),
		m_bearish(bearish),m_bullish(bullish),m_maxOpenQuantity(maxOpenQuantity)
	{
	}
	ArbitrageBusinessArgument(ArbitrageContracts const& contract,
		Band const& band,int maxOpenQuantity)
		:m_type(band.IsSameType(Band::CreateBearish()) ? JUST_BEARISH : JUST_BULLISH),
		m_contract(contract),m_openBalance(0),m_profitBalance(0),
		m_bearish(m_type == JUST_BEARISH ? band : Band::CreateBearish()),
		m_bullish(m_type == JUST_BULLISH ? band : Band::CreateBullish()),
		m_maxOpenQuantity(maxOpenQuantity)
	{
	}

	ArbitrageBusinessArgument(ArbitrageBusinessArgument const& other)
		:m_type(other.m_type),
		m_contract(other.m_contract),m_openBalance(other.m_openBalance),m_profitBalance(other.m_profitBalance),
		m_bearish(other.m_bearish),
		m_bullish(other.m_bullish),
		m_maxOpenQuantity(other.m_maxOpenQuantity)
	{
	}

	int GetType() const { return m_type; }
	ArbitrageContracts const& GetContracts() const { return m_contract; }
	double GetOpenBalance() const { return m_openBalance; }
	double GetProfitBalance() const { return m_profitBalance; }
	Band const& GetBearish() const { return m_bearish; }
	Band const& GetBullish() const { return m_bullish; }
	int GetMaxOpenQuantity() const { return m_maxOpenQuantity; }

	int GetMajor() const {return m_contract.GetMajor();}
	int GetMinor() const {return m_contract.GetMinor();}

	void UpdateBearish(Band const& band)
	{
		if (m_bearish.IsSameType(band))
		{
			m_bearish = band;
		}
	}

	void UpdateBullish(Band const& band)
	{
		if (m_bullish.IsSameType(band))
		{
			m_bullish = band;
		}
	}

	void UpdateByBollinger(Bollinger const& bollinger)
	{
		m_bearish.UpdateByBollinger(bollinger);
		m_bullish.UpdateByBollinger(bollinger);
	}

	void SetOpenBalance(double openBalance)
	{
		m_openBalance = openBalance;
	}

	void SetProfitBalance(double profitBalance)
	{
		m_profitBalance = profitBalance;
	}
private:
	int m_type;
	ArbitrageContracts m_contract;
	double m_openBalance;
	double m_profitBalance;
	Band m_bearish;
	Band m_bullish;
	int m_maxOpenQuantity;
};

struct ArbitrageBusinessSnapshot
{
	char const* statusName;
	int minorDirection;
	int majorPosition;
	int minorPosition;
	double profit;
	bool operator==(ArbitrageBusinessSnapshot const& other) const
	{
		return memcmp(this,&other,sizeof(ArbitrageBusinessSnapshot)) == 0;
	}
	bool operator!=(ArbitrageBusinessSnapshot const& other) const
	{
		return !operator==(other);
	}
};
 
class ArbitrageBusiness : public AutoUpdateUIBusiness<ArbitrageBusiness>
{
public:
	typedef ArbitrageBusinessArgument Argument;
	typedef ArbitrageBusinessSnapshot Snapshot;
public:

	ArbitrageBusiness(Customer& customer,
		Argument const& argument,
		MarketStorage const& marketStorage,
		QueueContext& queueContext, 
		DealingRepository& dealingRepository,
		CancellationRepository& cancellationRepository);

	virtual ~ArbitrageBusiness();

	Argument const& GetArgument() const { return m_argument; }
	int GetMajor() const { return m_argument.GetContracts().GetMajor(); }
	int GetMinor() const { return m_argument.GetContracts().GetMinor(); }
	char const* GetMajorCode() const;
	char const* GetMinorCode() const;
	char const* GetFartherCode() const;
	char const* GetNearbyCode() const;
	ClassicBusinessPosition const& GetPositions() const { return m_positions; }
	bool IsEditable() const;
	void UpdateBandByBollinger();

	void Init();
	bool TryOpen();

	void OpenMinor();
	void CancelMinorOpen();
	void OpenMajor();
	void CancelMajorOpen();
	
	void OffsetMinor();
	void CancelMinorOffset();
	void OffsetMajorByLimitPrice();

	void OffsetAll();

	bool CanOpen() const;
	bool CanOffset() const;
	bool CanStop() const;
	bool IsMarketCloseLimit();

	Snapshot GetSnapshot();
	void UpdateUI(Snapshot const& snapshot);

	bool HasSamePosition() const;
	bool IsMinorZero() const;
	bool IsZero() const;

private:

	virtual void DoOnInit();
	virtual void DoOnMarket(Market const& market);
	virtual void DoOnPendingMarket(PendingMarket const& pendingMarket);;
	virtual void DoOnDealing(Dealing const& dealing);

	virtual void DoOnTimeout(RequestTimeout const& requestTimeout);
	virtual void DoOnTrade(Trade const& trade);
	virtual void DoOnCommand(Command const& command);
	virtual void DoOnExchangeStatus(int exchangeStatus);

	void UpdateArgument(Argument const& argument);
	ArbitrageLegs const* GetCurrentLegs() const { return m_currentLegs;}
	Order CreateOrder(Leg const& leg,double price, int quantity);
	Dealing& PushOrder(Order const& order);
	Cancellation& PushCancellation(Cancellation const& cancellation);
	ArbitrageBusinessArgument m_argument;
	MarketStorage const& m_marketStorage;
	QueueContext& m_queueContext;
	DealingRepository& m_dealingRepository;
	CancellationRepository& m_cancellationRepository;
	ArbitrageBusinessStatusMachine::StatusMachineT* m_machine;
	ArbitrageLegs m_bearishLegs;
	ArbitrageLegs m_bullishLegs;
	ArbitrageLegs const* m_currentLegs;
	ClassicBusinessPosition m_positions;

	int m_minorOpenQuantity;

	DealingHistory m_minorOpenHistory;
	DealingHistory m_majorOpenHistory;
	DealingHistory m_minorOffsetHistory;
	DealingHistory m_majorOffsetHistory;

	Bollingers const* m_bollingers;
};
